package service

import (
	"bytes"
	"context"
	"dubbo.apache.org/dubbo-go/v3/config"
	"encoding/json"
	"fmt"
	"github.com/disintegration/imaging"
	"github.com/dubbogo/grpc-go/codes"
	"github.com/dubbogo/grpc-go/status"
	_ "github.com/go-sql-driver/mysql"
	ffmpeg "github.com/u2takey/ffmpeg-go"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
	"video/api"
)

var commonProvider *api.CommonClientImpl

type VideoServerImpl struct {
	api.UnimplementedVideoProviderServer
}

func OK(value []*api.Video, size int64) (*api.VideoList, error) {
	return &api.VideoList{
		List: value,
		Size: size,
	}, nil
}

func FAIL(err error) (*api.VideoList, error) {
	return nil, err
}

// 从视频文件中提取封面
func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string, err error) {
	buf := bytes.NewBuffer(nil)
	err = ffmpeg.Input(videoPath).
		Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}).
		Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
		WithOutput(buf, os.Stdout).
		Run()
	if err != nil {
		log.Fatal("生成缩略图失败：", err)
		return "", err
	}

	img, err := imaging.Decode(buf)
	if err != nil {
		log.Fatal("生成缩略图失败：", err)
		return "", err
	}

	err = imaging.Save(img, snapshotPath)
	if err != nil {
		log.Fatal("生成缩略图失败：", err)
		return "", err
	}

	names := strings.Split(snapshotPath, "\\")
	snapshotName = names[len(names)-1]
	return
}

func (s *VideoServerImpl) AddVideo(c context.Context, video *api.Video) (*api.VideoList, error) {
	// ---------------------- 视频文件处理开始 -----------------------------
	// 解压文件
	//var filepoint multipart.File
	//err := json.Unmarshal(video.Data, &filepoint) // ? 是这样解压么
	//if err != nil {
	//	return FAIL(err)
	//}
	//defer filepoint.Close()

	// 文件基本信息
	uploadPath := "/usr/local/upload"
	httpPath := "http://139.159.188.186:2221"

	filePath := "/" + strconv.FormatInt(video.UserId, 10) + "/" + strconv.FormatInt(time.Now().Unix(), 10) + ".mp4"

	_ = os.MkdirAll(uploadPath+"/video"+"/"+strconv.FormatInt(video.UserId, 10)+"/", 755)
	f, err := os.Create(uploadPath + "/video" + filePath)
	if err != nil {
		return FAIL(err)
	}
	defer f.Close()
	f.Write(video.Data)
	video.PlayUrl = httpPath + "/video" + filePath
	//// ---------------------- 视频文件处理结束 -----------------------------

	// ------------------------- 封面提取 --------------------------------
	_ = os.MkdirAll(uploadPath+"/image"+"/"+strconv.FormatInt(video.UserId, 10)+"/", 755)
	coverPath := "/" + strconv.FormatInt(video.UserId, 10) + "/" + strconv.FormatInt(time.Now().Unix(), 10) + ".png"
	_, err = GetSnapshot(uploadPath+"/video"+filePath, uploadPath+"/image"+coverPath, 1)
	if err != nil {
		return FAIL(err)
	}
	video.CoverUrl = httpPath + "/image" + coverPath
	// ------------------------- 提取完毕 --------------------------------

	// ------------------------- 数据库更新 ------------------------------
	_, err = commonProvider.Update(c, &api.Value{
		Database: "douyin",
		Table:    "video",
		Key:      []string{"user_id", "play_url", "cover_url", "title", "create_time"},
		Value:    []string{strconv.FormatInt(video.UserId, 10), video.PlayUrl, video.CoverUrl, video.Title, strconv.FormatInt(time.Now().Unix(), 10)},
	})

	if err != nil {
		return FAIL(err)
	}

	return OK(nil, 0)
}

func (s *VideoServerImpl) DelVideo(c context.Context, video *api.Video) (*api.VideoList, error) {
	// 参数校验
	if video.Id == 0 {
		return FAIL(status.Errorf(codes.FailedPrecondition, "parameter error"))
	}

	// 删除
	//sql_str := "DELETE FROM video WHERE id = ?"
	//_, err := db.Exec(sql_str, video.Id)
	_, err := commonProvider.Del(c, &api.Query{
		Database: "douyin",
		Table:    "video",
		Where: []*api.Query_Condition{
			{
				Key:   []string{"id"},
				Value: []string{strconv.FormatInt(video.Id, 10)},
				Type:  []api.Query_Condition_ConditionType{api.Query_Condition_NE},
			},
		},
	})
	if err != nil {
		return nil, err
	}

	//删除文件
	err = os.Remove(video.PlayUrl)
	if err != nil {
		return nil, err
	}

	if err != nil {
		return FAIL(err)
	}

	return OK(nil, 0)
}

func (s *VideoServerImpl) GetVideoById(c context.Context, video *api.Video) (*api.VideoList, error) {
	// 参数校验
	if video.Id == 0 {
		return FAIL(status.Errorf(codes.FailedPrecondition, "parameter error"))
	}

	// 查询
	//sql_str := "select id, user_id, play_url, cover_url, title, create_time from video where id = ?"
	//query, err := db.Query(sql_str, video.Id)
	get, err := commonProvider.Get(c, &api.Query{
		Database: "douyin",
		Table:    "video",
		Where: []*api.Query_Condition{
			{
				Key:   []string{"id"},
				Value: []string{strconv.FormatInt(video.Id, 10)},
				Type:  []api.Query_Condition_ConditionType{api.Query_Condition_NE},
			},
		},
		Result: api.Query_VIDEO,
	})
	if err != nil {
		return FAIL(err)
	}

	// 读取
	var result []*api.Video
	err = json.Unmarshal(get.Value, &result)
	if err != nil {
		return FAIL(err)
	}
	return OK(result, 0)
}

func (s *VideoServerImpl) GetVideoByUserId(c context.Context, video *api.Video) (*api.VideoList, error) {
	// 参数校验
	if video.UserId == 0 {
		return FAIL(status.Errorf(codes.FailedPrecondition, "parameter error"))
	}

	// 查询
	//sql_str := "select id, user_id, play_url, cover_url, title, create_time from video where id = ?"
	//query, err := db.Query(sql_str, video.Id)
	get, err := commonProvider.Get(c, &api.Query{
		Database: "douyin",
		Table:    "video",
		Where: []*api.Query_Condition{
			{
				Key:   []string{"user_id"},
				Value: []string{strconv.FormatInt(video.UserId, 10)},
				Type:  []api.Query_Condition_ConditionType{api.Query_Condition_NE},
			},
		},
		Result: api.Query_VIDEO,
	})
	if err != nil {
		return FAIL(err)
	}

	// 读取
	var result []*api.Video
	err = json.Unmarshal(get.Value, &result)
	if err != nil {
		return FAIL(err)
	}
	return OK(result, 0)
}

func (s *VideoServerImpl) GetVideoRecently(c context.Context, video *api.Video) (*api.VideoList, error) {

	var latestTime int64
	// 参数校验，修改latestTime数值
	if video == nil || video.CreateTime == 0 {
		latestTime = time.Now().Unix()
	} else {
		latestTime = video.CreateTime
	}

	// 查询
	//sql_str := "select id, user_id, play_url, cover_url, title, create_time from video where create_time <= ? order by create_time limit 30"
	//query, err := db.Query(sql_str, latestTime)
	get, err := commonProvider.Get(c, &api.Query{
		Database: "douyin",
		Table:    "video",
		Where: []*api.Query_Condition{
			{
				Key:   []string{"create_time"},
				Value: []string{strconv.FormatInt(latestTime, 10)},
				Type:  []api.Query_Condition_ConditionType{api.Query_Condition_LE},
			},
		},
		OrderBy: []*api.Query_Order{
			{
				Key:  "create_time",
				Type: api.Query_Order_DESC,
			},
		},
		Result: api.Query_VIDEO,
	})
	if err != nil {
		return FAIL(err)
	}

	// 读取
	var result []*api.Video
	//for query.Next() {
	//	newVideo := &api.Video{}
	//
	//	err := query.Scan(
	//		&newVideo.Id,
	//		&newVideo.UserId,
	//		&newVideo.PlayUrl,
	//		&newVideo.CoverUrl,
	//		&newVideo.Title,
	//		&newVideo.CreateTime,
	//	)
	//	if err != nil {
	//		return FAIL(err)
	//	}
	//
	//	result = append(result, newVideo)
	//}
	err = json.Unmarshal(get.Value, &result)
	if err != nil {
		return FAIL(err)
	}
	return OK(result, 0)
}

func init() {
	// mysql
	//db, err = sql.Open("mysql", "root:for.nothing@tcp(localhost:3306)/douyin")
	//if err != nil {
	//	fmt.Println("Open mysql error:", err)
	//	os.Exit(-1)
	//}
	//fmt.Println("Connect to Database Success! ... ")

	//公共服务
	commonProvider = new(api.CommonClientImpl)

	// 注册服务
	config.SetProviderService(&VideoServerImpl{})
	config.SetConsumerService(commonProvider)
}
